// Package handler 实现 gRPC 服务的具体业务逻辑
package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/google/uuid"
	"math"
	"math/rand"
	"shop/common/global"
	"shop/common/model"
	"shop/common/untils"
	"shop/shop_grpc/proto/shop"
	"time"
)

// Server 结构体实现了 shop.ShopServer 接口
// 初始化: 创建 Server 实例即可使用，无需额外初始化
// 示例: server := &Server{}
// 该结构体继承自 shop.UnimplementedShopServer，需要实现相关接口方法

type Server struct {
	shop.UnimplementedShopServer
}

// CreateSeckillGoods 创建秒杀商品
// 功能: 创建秒杀活动商品，扣减原商品库存，并同步秒杀库存到Redis
// 参数:
//   - ctx: 上下文对象
//   - in: 包含商品ID、秒杀价格、秒杀库存、开始时间、结束时间等信息
// 返回值:
//   - *shop.CreateSeckillGoodsResponse: 包含创建成功的秒杀商品ID
//   - error: 错误信息
func (s *Server) CreateSeckillGoods(_ context.Context, in *shop.CreateSeckillGoodsRequest) (*shop.CreateSeckillGoodsResponse, error) {
	var goods model.Goods
	// 开启数据库事务
	be := global.DB.Begin()
	// 查询商品信息
	err := be.Where("id = ?", in.GoodsId).Find(&goods).Error
	if err != nil {
		return nil, fmt.Errorf("查询商品信息失败")
	}
	// 检查商品是否存在
	if goods.Id == 0 {
		return nil, fmt.Errorf("当前商品不存在")
	}
	// 检查库存是否充足
	if in.SeckillStock > int64(goods.GoodStock) {
		return nil, fmt.Errorf("当前库存不足")
	}
	// 扣减原商品库存
	newStock := int64(goods.GoodStock) - in.SeckillStock
	err = be.Model(&model.Goods{}).Where("id=?", in.GoodsId).Update("good_stock", newStock).Error
	if err != nil {
		return nil, fmt.Errorf("扣减库存失败")
	}
	// 设置时区并解析时间
	loc, _ := time.LoadLocation("Asia/Shanghai")
	beginTime, _ := time.ParseInLocation(time.DateTime, in.BeginTime, loc)
	endTime, _ := time.ParseInLocation(time.DateTime, in.EndTime, loc)
	// 创建秒杀商品
	seckillGood := model.SeckillGoods{
		GoodId:       int32(in.GoodsId),
		SeckillPrice: float64(in.SeckillPrice),
		SeckillStock: int32(in.SeckillStock),
		BeginTime:    beginTime,
		EndTime:      endTime,
	}
	// 保存秒杀商品到数据库
	err = be.Create(&seckillGood).Error
	if err != nil {
		be.Rollback() // 保存失败回滚事务
		return nil, fmt.Errorf("秒杀商品创建失败")
	}
	// 同步秒杀库存到Redis
	if err = untils.SyncSeckillGoods(int(seckillGood.Id), int(in.SeckillStock)); err != nil {
		be.Rollback() // 同步失败回滚事务
		return nil, fmt.Errorf("同步秒杀商品虚拟库存失败")
	}
	// 提交事务
	be.Commit()
	return &shop.CreateSeckillGoodsResponse{
		SeckillId: int64(seckillGood.Id),
	}, nil
}

// FindGoods 查询商品详情
// 功能: 根据商品ID查询商品信息，优先从Redis缓存获取，缓存不存在则从数据库查询并同步到缓存
// 参数:
//   - ctx: 上下文对象
//   - in: 包含商品ID的查询请求
// 返回值:
//   - *shop.FindGoodsResponse: 包含商品标题、图片、价格、库存等信息
//   - error: 错误信息
func (s *Server) FindGoods(_ context.Context, in *shop.FindGoodsRequest) (*shop.FindGoodsResponse, error) {
	var goods model.Goods
	key := fmt.Sprintf("goods_id=%d", in.GoodsId)
	// 检查Redis缓存中是否存在该商品
	result, _ := global.Rdb.Exists(global.Ctx, key).Result()
	if result != 0 {
		// 从缓存获取商品信息
		s2, _ := global.Rdb.Get(global.Ctx, key).Result()
		err := json.Unmarshal([]byte(s2), &goods)
		if err != nil {
			return nil, fmt.Errorf("系统繁忙")
		}
	} else {
		// 使用分布式锁防止缓存击穿
		keys := fmt.Sprintf("lock_goods_id=%d", in.GoodsId)
		untils.SyncDataLock(keys, uuid.NewString(), time.Second)
		// 从数据库查询商品信息
		err := goods.Find(int(in.GoodsId))
		if err != nil {
			return nil, fmt.Errorf("商品信息查询失败")
		}
		// 检查商品是否存在
		if goods.Id == 0 {
			return nil, fmt.Errorf("当前商品不存在")
		}
		// 将商品信息同步到Redis缓存
		marshal, _ := json.Marshal(goods)
		global.Rdb.Set(global.Ctx, key, marshal, time.Hour)
		// 释放分布式锁
		untils.ReleaseDataLock(keys)
	}
	return &shop.FindGoodsResponse{
		Title:     goods.Title,
		Image:     goods.Image,
		GoodPrice: float32(goods.GoodPrice),
		GoodStock: int64(goods.GoodStock),
	}, nil
}

// CreateSeckillOrder 创建秒杀订单
// 功能: 验证用户和商品信息，扣减Redis虚拟库存，创建秒杀订单并发送到消息队列异步处理
// 参数:
//   - ctx: 上下文对象
//   - in: 包含用户ID、秒杀商品ID、购买数量、地址ID等信息
// 返回值:
//   - *shop.CreateSeckillOrderResponse: 包含订单号和支付宝支付链接
//   - error: 错误信息
func (s *Server) CreateSeckillOrder(_ context.Context, in *shop.CreateSeckillOrderRequest) (*shop.CreateSeckillOrderResponse, error) {
	// 1. 验证用户和商品信息
	var user model.User
	err := global.DB.Where("id = ?", in.UserId).First(&user).Error
	if err != nil || user.Id == 0 {
		return nil, fmt.Errorf("请先登录")
	}

	var seckillGood model.SeckillGoods
	err = global.DB.Where("id = ?", in.SeckillGoodsId).First(&seckillGood).Error
	if err != nil || seckillGood.Id == 0 {
		return nil, fmt.Errorf("秒杀商品不存在")
	}

	// 2. 验证秒杀活动状态
	t := time.Now()
	if t.Before(seckillGood.BeginTime) {
		return nil, fmt.Errorf("当前活动未开始")
	}
	if t.After(seckillGood.EndTime) {
		return nil, fmt.Errorf("当前活动已结束")
	}
	if seckillGood.SeckillStatus != 1 {
		return nil, fmt.Errorf("秒杀活动已停用")
	}

	// 3. 扣减Redis虚拟库存
	stockSuccess := false
	// 优化：先检查库存是否充足
	currentStock, err := untils.GetSeckillGoodsStock(int(in.SeckillGoodsId))
	if err != nil {
		return nil, fmt.Errorf("获取库存信息失败: %v", err)
	}
	if currentStock < int(in.Num) {
		return nil, fmt.Errorf("商品库存不足")
	}

	// 批量扣减库存
	for i := 0; i < int(in.Num); i++ {
		if !untils.UpdateSeckillGoodsStock(int(in.SeckillGoodsId)) {
			// 如果任何一次扣减失败，回滚之前扣减的库存
			for j := 0; j < i; j++ {
				untils.ReturnSeckillGoodsStock(int(in.SeckillGoodsId), 1)
			}
			// 再次检查库存，确认是否真正不足
			latestStock, _ := untils.GetSeckillGoodsStock(int(in.SeckillGoodsId))
			if latestStock < 0 {
				return nil, fmt.Errorf("系统繁忙，请稍后再试")
			}
			return nil, fmt.Errorf("商品库存不足")
		}
	}
	stockSuccess = true

	// 4. 创建订单信息，但不保存到数据库
	orderNo := "SY" + time.Now().Format("20060102150405") + fmt.Sprintf("%d", rand.Intn(100000))
	a := seckillGood.SeckillPrice * float64(in.Num)
	// 将浮点数乘以100，然后进行四舍五入
	rounded := math.Round(a * 100)
	// 再除以100，得到保留两位小数的浮点数
	SkPrice := rounded / 100

	var address model.Address
	if in.AddressId == 0 {
		global.DB.Where("status = 2").Find(&address)
		in.AddressId = int64(address.Id)
	}

	order := model.Order{
		OrderNo:        orderNo,
		SeckillGoodsId: int32(in.SeckillGoodsId),
		SeckillPrice:   SkPrice,
		UserId:         int32(in.UserId),
		Num:            int32(in.Num),
		Status:         2, // 设置为未支付状态
		AddressId:      int32(in.AddressId),
	}

	// 5. 发送到消息队列，由异步处理创建订单
	err = untils.PublishMQ(&order)
	if err != nil {
		// MQ发送失败，回滚库存
		if stockSuccess {
			untils.ReturnSeckillGoodsStock(int(in.SeckillGoodsId), int(in.Num))
		}
		return nil, fmt.Errorf("系统繁忙，请稍后再试: %v", err)
	}

	// 6. 生成支付宝链接并返回订单信息
	alipay := untils.AliPay{}
	return &shop.CreateSeckillOrderResponse{
		Order:  order.OrderNo,
		PayUrl: alipay.AliPay(order.OrderNo, fmt.Sprintf("%.2f", SkPrice)),
	}, nil
}

// OrderPay 订单支付
// 功能: 更新订单支付状态，更新秒杀商品库存
// 参数:
//   - ctx: 上下文对象
//   - in: 包含订单号和支付状态的请求
// 返回值:
//   - *shop.OrderPayResponse: 支付结果响应
//   - error: 错误信息
func (s *Server) OrderPay(_ context.Context, in *shop.OrderPayRequest) (*shop.OrderPayResponse, error) {
	// 开始事务
	be := global.DB.Begin()
	defer func() {
		// 处理panic，确保事务回滚
		if r := recover(); r != nil {
			be.Rollback()
		}
	}()

	// 先查询订单是否存在
	var order model.Order
	err := be.Where("orderNo = ?", in.OrderNo).First(&order).Error
	if err != nil {
		be.Rollback()
		return nil, fmt.Errorf("查询秒杀订单信息失败: %v", err)
	}

	// 更新订单状态
	err = be.Model(model.Order{}).Where("orderNo = ?", in.OrderNo).Update("status", in.Status).Update("pay_time", time.Now()).Error
	if err != nil {
		be.Rollback()
		return nil, fmt.Errorf("订单状态更新失败: %v", err)
	}

	// 查询秒杀商品信息
	var seckillGood model.SeckillGoods
	err = be.Where("id = ?", order.SeckillGoodsId).First(&seckillGood).Error
	if err != nil {
		be.Rollback()
		return nil, fmt.Errorf("秒杀商品信息异常: %v", err)
	}

	// 更新库存
	seckillGood.SeckillStock -= order.Num
	err = be.Model(model.SeckillGoods{}).Where("id = ?", order.SeckillGoodsId).Update("seckill_stock", seckillGood.SeckillStock).Error
	if err != nil {
		be.Rollback()
		return nil, err
	}

	// 提交事务
	be.Commit()
	return &shop.OrderPayResponse{}, nil
}

// AddShopCar 添加商品到购物车
// 功能: 将商品添加到购物车，同时更新Redis缓存和数据库
// 参数:
//   - ctx: 上下文对象
//   - in: 包含用户ID、商品ID、数量的请求
// 返回值:
//   - *shop.AddShopCarResponse: 添加结果响应
//   - error: 错误信息
func (s *Server) AddShopCar(_ context.Context, in *shop.AddShopCarRequest) (*shop.AddShopCarResponse, error) {
	// 构建Redis键
	key := fmt.Sprintf("user_id%d:goods_id%d", in.UserId, in.GoodsId)
	// 检查Redis中是否已存在该购物车项
	result, _ := global.Rdb.Exists(global.Ctx, key).Result()
	if result == 0 {
		// 创建新的购物车项
		global.Rdb.HMSet(global.Ctx, key,
			"good_id", in.GoodsId,
			"user_id", in.UserId,
			"num", in.Num)
	} else {
		// 更新已有购物车项数量
		a, _ := global.Rdb.HIncrBy(global.Ctx, key, "num", int64(in.Num)).Result()
		if a == 0 {
			return nil, fmt.Errorf("购物车添加失败")
		}
	}
	// 更新数据库中的购物车信息
	var shopCar model.ShopCar
	err := global.DB.Where("user_id = ? and goods_id = ?", in.UserId, in.GoodsId).Find(&shopCar).Error
	if err != nil {
		return nil, fmt.Errorf("购物车查询失败")
	}
	if shopCar.Id == 0 {
		// 数据库中不存在，创建新记录
		shopCar.Num = in.Num
		shopCar.UserId = int32(in.UserId)
		shopCar.GoodsId = int32(in.GoodsId)
		err = global.DB.Create(&shopCar).Error
		if err != nil {
			return nil, fmt.Errorf("购物车添加失败")
		}
	} else {
		// 数据库中已存在，更新数量
		shopCar.Num += in.Num
		err = global.DB.Model(&model.ShopCar{}).Where("id = ?", shopCar.Id).Update("num", shopCar.Num).Error
		if err != nil {
			return nil, fmt.Errorf("购物车添加失败")
		}
	}

	return &shop.AddShopCarResponse{}, nil
}

// ListGoods 列出商品列表
// 功能: 查询商品列表，支持根据标题模糊搜索
// 参数:
//   - ctx: 上下文对象
//   - in: 包含可选标题搜索条件的请求
// 返回值:
//   - *shop.ListGoodsResponse: 包含商品列表的响应
//   - error: 错误信息
func (s *Server) ListGoods(_ context.Context, in *shop.ListGoodsRequest) (*shop.ListGoodsResponse, error) {
	var goods []model.Goods
	//var err error
	
	// 使用ES进行商品列表查询，支持高亮显示
	// 页码和每页数量，这里暂时使用固定值
	//page := 1
	//pageSize := 100
	//goods, _, err = untils.SearchGoodsInES(in.Title, page, pageSize)
	//if err != nil {
	//	// 如果ES查询失败，降级到MySQL查询
	//	if in.Title == "" {
	//		// 查询所有商品
	//		global.DB.Find(&goods)
	//	} else {
	//		// 根据标题模糊搜索商品
	//		global.DB.Where("title like ?", "%"+in.Title+"%").Find(&goods)
	//	}
	//}
	// 构建响应数据
	var list []*shop.List
	for _, good := range goods {
		lists := shop.List{
			Title:     good.Title, // 这里会包含高亮标记<em>...</em>
			Image:     good.Image,
			GoodPrice: float32(good.GoodPrice),
			GoodStock: good.GoodStock,
		}
		list = append(list, &lists)
	}
	return &shop.ListGoodsResponse{
		List: list,
	}, nil
}
